#include "linux-alsa-sound.h"
#include <stdio.h>
#include "wav-file.h"
/**
 * 构造函数
 */
LinuxAlsaSound::LinuxAlsaSound()
{
	this->pcmHandle = nullptr;
	this->pcmParams = nullptr;
}

/**
 * 打开alse设备
*/
bool LinuxAlsaSound::open(const string &pcmDevice, snd_pcm_stream_t stream)
{
	int err = snd_pcm_open(&pcmHandle, pcmDevice.c_str(), stream, 0);
	if(err)
	{
		fprintf(stderr, "Fail to snd_pcm_open %s:%s\n", pcmDevice.c_str(), snd_strerror(err));
		return false;
	}
	return true;
}

bool LinuxAlsaSound::setParams(pcm_hw_params_t *parm)
{
	// 1初始化内存
	int err = snd_pcm_hw_params_malloc(&pcmParams);
	if(err)
	{
		fprintf(stderr, "Fail to snd_pcm_hw_params_malloc,err%s\n", snd_strerror(err));
		return false;
	}

	// 2参数初始化 填充默认值
	err = snd_pcm_hw_params_any(pcmHandle, pcmParams);
	if(err)
	{
		fprintf(stderr, "Fail to snd_pcm_hw_params_any,err%s\n", snd_strerror(err));
		return false;
	}

	// 3设置数据帧的存储的存储方式
	// 	pcm    - PCM handle
	// params - 硬件参数结构体
	// access - 交错模式:SND_PCM_ACCESS_RW_INTERLEAVED/非交错模式:SND_PCM_ACCESS_RW_NONINTERLEAVED
	err = snd_pcm_hw_params_set_access(pcmHandle, pcmParams, parm->access);
	if(err)
	{
		fprintf(stderr, "Fail to snd_pcm_hw_params_set_access,err%s\n", snd_strerror(err));
		return false;
	}

	/**
	 * 设置格式
	 * format - 
         8位格式         - SND_PCM_FORMAT_S8
         有符号16位小端   - SND_PCM_FORMAT_S16_LE
         有符号16位大端   - SND_PCM_FORMAT_S16_BE
         无符号16位小端   - SND_PCM_FORMAT_U16_LE
返回值:
	*/
	err = snd_pcm_hw_params_set_format(pcmHandle, pcmParams, parm->format);
	if(err)
	{
		fprintf(stderr, "Fail to snd_pcm_hw_params_set_format,err%s\n", snd_strerror(err));
		return false;
	}

	/**
	 * 设置通道数
	 * val    -  2  - 双通道(stereo)
          1  - 单通道(mono)
	*/
	err = snd_pcm_hw_params_set_channels(pcmHandle, pcmParams, parm->channels);
	if(err)
	{
		fprintf(stderr, "Fail to snd_pcm_hw_params_set_channels,err%s\n", snd_strerror(err));
		return false;
	}

	/**
	 * 设置采样位数
	*/
	err = snd_pcm_hw_params_set_rate_near(pcmHandle,
	                                      pcmParams,
	                                      &parm->sampleRate,
	                                      NULL);
	if(err)
	{
		fprintf(stderr, "Fail to snd_pcm_hw_params_set_rate_near,err%s\n", snd_strerror(err));
		return false;
	}

	/**
	 * 	          
	 * 设置采样帧数
	*/
	err = snd_pcm_hw_params_set_period_size_near(pcmHandle,
	                                             pcmParams,
	                                             &parm->frames,
	                                             NULL);
	if(err)
	{
		fprintf(stderr, "Fail to snd_pcm_hw_params_set_period_size_near,err%s\n", snd_strerror(err));
		return false;
	}

	//整体参数设置
	err = snd_pcm_hw_params(pcmHandle, pcmParams);
	if(err)
	{
		fprintf(stderr, "Fail to snd_pcm_hw_params,err%s\n", snd_strerror(err));
		return false;
	}

	//通道
	this->channels = parm->channels;
	//采样位数
	this->sampleBits = parm->sampleBits;

	return true;
}

//录制音频
bool LinuxAlsaSound::record(uint32_t mtime, vector<uint8_t> &pcmData)
{
	snd_pcm_uframes_t peroidFrames;
	int               err = snd_pcm_hw_params_get_period_size(pcmParams,
                                                &peroidFrames, NULL);
	if(err)
	{
		fprintf(stderr, "Fail to snd_pcm_hw_params_get_period_size,err%s\n", snd_strerror(err));
		return false;
	}
	//一个周期多少帧
	// printf("peroid frames:%ld\n", peroidFrames);

	//计算一个周期的大小=帧数*通道数*采样位数
	uint32_t peroidSizeBytes = peroidFrames * channels * sampleBits / 8;
	// printf("peroid size bytes:%d\n", peroidSizeBytes);

	//获取一个周期的时间
	uint32_t peroidTime;
	err = snd_pcm_hw_params_get_period_time(pcmParams, &peroidTime, NULL);
	if(err)
	{
		fprintf(stderr, "Fail to snd_pcm_hw_params_get_period_time,err%s\n", snd_strerror(err));
		return false;
	}
	// printf("peroid Time:%dus \n", peroidTime);

	uint32_t peroidCount = mtime * 1000 / peroidTime;
	// printf("peroid Count:%dus \n", peroidCount);

	//设置vector大小
	pcmData.resize(peroidSizeBytes * peroidCount);
	for(int i = 0; i < peroidCount; i++)
	{
		// 交错模式下录制接口; 对于非交错模式, 使用snd_pcm_readn
		snd_pcm_sframes_t ret = snd_pcm_readi(pcmHandle,
		                                      pcmData.data() + i * peroidSizeBytes,
		                                      peroidFrames);
		if(ret != peroidFrames)
		{
			fprintf(stderr, "Fail to snd_pcm_readi,err%s\n", snd_strerror(ret));
			return false;
		}
	}

	return true;
}

bool LinuxAlsaSound::recordPCMData(uint32_t time, const string &filename)
{
	FILE *fp = fopen(filename.c_str(), "w");
	if(!fp)
	{
		fprintf(stderr, "Fail to fopen %s:%s\n", filename.c_str(), strerror(errno));
		fclose(fp);
		return false;
	}
	vector<uint8_t> pcmData;
	bool            ok = record(time * 1000, pcmData);
	if(!ok)
	{
		fclose(fp);
		return false;
	}

	int ret = fwrite(pcmData.data(), sizeof(uint8_t), pcmData.size(), fp);
	if(ret != pcmData.size())
	{
		fprintf(stderr, "Fail to fwrite :%s\n", strerror(errno));
		fclose(fp);
		return false;
	}
	fclose(fp);
	return true;
}

bool LinuxAlsaSound::recordWAVData(uint32_t time, const string &filename)
{
	//1录入数据
	vector<uint8_t> pcmData;
	bool            ok = record(time * 1000, pcmData);
	if(!ok)
	{
		return false;
	}

	//获取采样率
	uint32_t sampleRate;
	int      err = snd_pcm_hw_params_get_rate(pcmParams, &sampleRate, NULL);
	if(err)
	{
		fprintf(stderr, "Fail to snd_pcm_hw_params_get_rate,err:%s\n", snd_strerror(err));
		return false;
	}

	//printf("record sampleRate:%d\n",sampleRate);

	WavFile file(filename);
	file.setParams(sampleRate, channels, sampleBits);
	file.writeData(pcmData.data(), pcmData.size());

	return true;
}

void LinuxAlsaSound::pause(void)
{
	snd_pcm_drain(pcmHandle);
}

void LinuxAlsaSound::resume(void)
{
	snd_pcm_prepare(pcmHandle);
}
